/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

/* vim:set softtabstop=3 shiftwidth=3 tabstop=3 expandtab tw=72:
   $Id: MD4.java,v 1.9 2003/03/30 15:18:46 rsdio Exp $
  
   This version is derived from the version in GNU Crypto.
  
   MD4: The MD4 message digest algorithm.
   Copyright (C) 2002 The Free Software Foundation, Inc.
   Copyright (C) 2003  Casey Marshall <rsdio@metastatic.org>
  
   This file is a part of Jarsync.
  
   Jarsync is free software; you can redistribute it and/or modify it
   under the terms of the GNU General Public License as published by the
   Free Software Foundation; either version 2 of the License, or (at
   your option) any later version.
  
   Jarsync is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   General Public License for more details.
  
   You should have received a copy of the GNU General Public License
   along with Jarsync; if not, write to the
  
      Free Software Foundation, Inc.,
      59 Temple Place, Suite 330,
      Boston, MA  02111-1307
      USA
  
   Linking Jarsync statically or dynamically with other modules is
   making a combined work based on Jarsync.  Thus, the terms and
   conditions of the GNU General Public License cover the whole
   combination.  */

package io.milton.zsync;

import java.security.DigestException;
import java.security.MessageDigestSpi;

/**
 * <p>An implementation of Ron Rivest's MD4 message digest algorithm.
 * MD4 was the precursor to the stronger MD5
 * algorithm, and while not considered cryptograpically secure itself,
 * MD4 is in use in various applications. It is slightly faster than
 * MD5.</p>
 *
 * <p>This implementation is derived from the version of MD4 in <a
 * href="http://www.gnu.org/software/gnu-crypto/">GNU Crypto.</p>
 *
 * <p>References:</p>
 *
 * <ol>
 *    <li>The <a href="http://www.ietf.org/rfc/rfc1320.txt">MD4</a> Message-
 *    Digest Algorithm.<br>
 *    R. Rivest.</li>
 * </ol>
 *
 * @version $Revision: 1.9 $
 */
public class MD4 extends MessageDigestSpi implements Cloneable {

    // Constants and variables.
    // -----------------------------------------------------------------

    /**
     * An MD4 message digest is always 128-bits long, or 16 bytes.
     */
    public static final int DIGEST_LENGTH = 16;

    /**
     * The MD4 algorithm operates on 512-bit blocks, or 64 bytes.
     */
    public static final int BLOCK_LENGTH = 64;

    protected static final int A = 0x67452301;
    protected static final int B = 0xefcdab89;
    protected static final int C = 0x98badcfe;
    protected static final int D = 0x10325476;

    /* The four chaining variables. */
    protected int a, b, c, d;

    protected long count;

    protected final byte[] buffer;

    /**
     * Word buffer for transforming.
     */
    private final int[] X = new int[16];

    // Constructors.
    // -----------------------------------------------------------------

    /**
     * Trivial zero-argument constructor.
     */
    public MD4() {
        buffer = new byte[BLOCK_LENGTH];
        engineReset();
    }

    /**
     * Private constructor for cloning.
     */
    private MD4(MD4 that) {
        this();

        this.a = that.a;
        this.b = that.b;
        this.c = that.c;
        this.d = that.d;
        this.count = that.count;
        System.arraycopy(that.buffer, 0, this.buffer, 0, BLOCK_LENGTH);
    }

    // java.lang.Cloneable interface implementation --------------------

    public Object clone() {
        return new MD4(this);
    }

    // SPI instance methods.
    // -----------------------------------------------------------------

    protected int engineGetDigestLength() {
        return DIGEST_LENGTH;
    }

    public void engineUpdate(byte b) {
        // compute number of bytes still unhashed; ie. present in buffer
        int i = (int) (count % BLOCK_LENGTH);
        count++;
        buffer[i] = b;
        if (i == (BLOCK_LENGTH - 1)) {
            transform(buffer, 0);
        }
    }

    protected void engineUpdate(byte[] b, int offset, int len) {
        int n = (int) (count % BLOCK_LENGTH);
        count += len;
        int partLen = BLOCK_LENGTH - n;
        int i = 0;

        if (len >= partLen) {
            System.arraycopy(b, offset, buffer, n, partLen);
            transform(buffer, 0);
            for (i = partLen; i + BLOCK_LENGTH - 1 < len; i += BLOCK_LENGTH) {
                transform(b, offset + i);
            }
            n = 0;
        }

        if (i < len) {
            System.arraycopy(b, offset + i, buffer, n, len - i);
        }
    }

    /**
     * Pack the four chaining variables into a byte array.
     */
    protected byte[] engineDigest() {
        byte[] tail = padBuffer();
        engineUpdate(tail, 0, tail.length);
        byte[] digest = {
                (byte) a, (byte) (a >>> 8), (byte) (a >>> 16), (byte) (a >>> 24),
                (byte) b, (byte) (b >>> 8), (byte) (b >>> 16), (byte) (b >>> 24),
                (byte) c, (byte) (c >>> 8), (byte) (c >>> 16), (byte) (c >>> 24),
                (byte) d, (byte) (d >>> 8), (byte) (d >>> 16), (byte) (d >>> 24)
        };

        engineReset();

        return digest;
    }

    protected int engineDigest(byte[] out, int off, int len) throws DigestException {
        if (off < 0 || off + len >= out.length) {
            throw new DigestException();
        }
        System.arraycopy(engineDigest(), 0, out, off,
                Math.min(len, DIGEST_LENGTH));
        return Math.min(len, DIGEST_LENGTH);
    }

    /**
     * Reset the four chaining variables.
     */
    protected void engineReset() {
        a = A;
        b = B;
        c = C;
        d = D;
        count = 0;
    }

    /**
     * Pad the buffer by appending the byte 0x80, then as many zero bytes
     * to fill the buffer 8 bytes shy of being a multiple of 64 bytes, then
     * append the length of the buffer, in bits, before padding.
     */
    protected byte[] padBuffer() {
        int n = (int) (count % BLOCK_LENGTH);
        int padding = (n < 56) ? (56 - n) : (120 - n);
        byte[] pad = new byte[padding + 8];

        pad[0] = (byte) 0x80;
        long bits = count << 3;
        pad[padding++] = (byte) bits;
        pad[padding++] = (byte) (bits >>> 8);
        pad[padding++] = (byte) (bits >>> 16);
        pad[padding++] = (byte) (bits >>> 24);
        pad[padding++] = (byte) (bits >>> 32);
        pad[padding++] = (byte) (bits >>> 40);
        pad[padding++] = (byte) (bits >>> 48);
        pad[padding] = (byte) (bits >>> 56);

        return pad;
    }

    /**
     * Transform a 64-byte block.
     */
    protected void transform(byte[] in, int offset) {
        int aa, bb, cc, dd;

        for (int i = 0, n = 0; i < 16; i++) {
            X[i] = (in[offset++] & 0xff) |
                    (in[offset++] & 0xff) << 8 |
                    (in[offset++] & 0xff) << 16 |
                    (in[offset++] & 0xff) << 24;
        }

        aa = a;
        bb = b;
        cc = c;
        dd = d;

        // Round 1
        a += ((b & c) | ((~b) & d)) + X[0];
        a = a << 3 | a >>> (32 - 3);
        d += ((a & b) | ((~a) & c)) + X[1];
        d = d << 7 | d >>> (32 - 7);
        c += ((d & a) | ((~d) & b)) + X[2];
        c = c << 11 | c >>> (32 - 11);
        b += ((c & d) | ((~c) & a)) + X[3];
        b = b << 19 | b >>> (32 - 19);
        a += ((b & c) | ((~b) & d)) + X[4];
        a = a << 3 | a >>> (32 - 3);
        d += ((a & b) | ((~a) & c)) + X[5];
        d = d << 7 | d >>> (32 - 7);
        c += ((d & a) | ((~d) & b)) + X[6];
        c = c << 11 | c >>> (32 - 11);
        b += ((c & d) | ((~c) & a)) + X[7];
        b = b << 19 | b >>> (32 - 19);
        a += ((b & c) | ((~b) & d)) + X[8];
        a = a << 3 | a >>> (32 - 3);
        d += ((a & b) | ((~a) & c)) + X[9];
        d = d << 7 | d >>> (32 - 7);
        c += ((d & a) | ((~d) & b)) + X[10];
        c = c << 11 | c >>> (32 - 11);
        b += ((c & d) | ((~c) & a)) + X[11];
        b = b << 19 | b >>> (32 - 19);
        a += ((b & c) | ((~b) & d)) + X[12];
        a = a << 3 | a >>> (32 - 3);
        d += ((a & b) | ((~a) & c)) + X[13];
        d = d << 7 | d >>> (32 - 7);
        c += ((d & a) | ((~d) & b)) + X[14];
        c = c << 11 | c >>> (32 - 11);
        b += ((c & d) | ((~c) & a)) + X[15];
        b = b << 19 | b >>> (32 - 19);

        // Round 2.
        a += ((b & (c | d)) | (c & d)) + X[0] + 0x5a827999;
        a = a << 3 | a >>> (32 - 3);
        d += ((a & (b | c)) | (b & c)) + X[4] + 0x5a827999;
        d = d << 5 | d >>> (32 - 5);
        c += ((d & (a | b)) | (a & b)) + X[8] + 0x5a827999;
        c = c << 9 | c >>> (32 - 9);
        b += ((c & (d | a)) | (d & a)) + X[12] + 0x5a827999;
        b = b << 13 | b >>> (32 - 13);
        a += ((b & (c | d)) | (c & d)) + X[1] + 0x5a827999;
        a = a << 3 | a >>> (32 - 3);
        d += ((a & (b | c)) | (b & c)) + X[5] + 0x5a827999;
        d = d << 5 | d >>> (32 - 5);
        c += ((d & (a | b)) | (a & b)) + X[9] + 0x5a827999;
        c = c << 9 | c >>> (32 - 9);
        b += ((c & (d | a)) | (d & a)) + X[13] + 0x5a827999;
        b = b << 13 | b >>> (32 - 13);
        a += ((b & (c | d)) | (c & d)) + X[2] + 0x5a827999;
        a = a << 3 | a >>> (32 - 3);
        d += ((a & (b | c)) | (b & c)) + X[6] + 0x5a827999;
        d = d << 5 | d >>> (32 - 5);
        c += ((d & (a | b)) | (a & b)) + X[10] + 0x5a827999;
        c = c << 9 | c >>> (32 - 9);
        b += ((c & (d | a)) | (d & a)) + X[14] + 0x5a827999;
        b = b << 13 | b >>> (32 - 13);
        a += ((b & (c | d)) | (c & d)) + X[3] + 0x5a827999;
        a = a << 3 | a >>> (32 - 3);
        d += ((a & (b | c)) | (b & c)) + X[7] + 0x5a827999;
        d = d << 5 | d >>> (32 - 5);
        c += ((d & (a | b)) | (a & b)) + X[11] + 0x5a827999;
        c = c << 9 | c >>> (32 - 9);
        b += ((c & (d | a)) | (d & a)) + X[15] + 0x5a827999;
        b = b << 13 | b >>> (32 - 13);

        // Round 3.
        a += (b ^ c ^ d) + X[0] + 0x6ed9eba1;
        a = a << 3 | a >>> (32 - 3);
        d += (a ^ b ^ c) + X[8] + 0x6ed9eba1;
        d = d << 9 | d >>> (32 - 9);
        c += (d ^ a ^ b) + X[4] + 0x6ed9eba1;
        c = c << 11 | c >>> (32 - 11);
        b += (c ^ d ^ a) + X[12] + 0x6ed9eba1;
        b = b << 15 | b >>> (32 - 15);
        a += (b ^ c ^ d) + X[2] + 0x6ed9eba1;
        a = a << 3 | a >>> (32 - 3);
        d += (a ^ b ^ c) + X[10] + 0x6ed9eba1;
        d = d << 9 | d >>> (32 - 9);
        c += (d ^ a ^ b) + X[6] + 0x6ed9eba1;
        c = c << 11 | c >>> (32 - 11);
        b += (c ^ d ^ a) + X[14] + 0x6ed9eba1;
        b = b << 15 | b >>> (32 - 15);
        a += (b ^ c ^ d) + X[1] + 0x6ed9eba1;
        a = a << 3 | a >>> (32 - 3);
        d += (a ^ b ^ c) + X[9] + 0x6ed9eba1;
        d = d << 9 | d >>> (32 - 9);
        c += (d ^ a ^ b) + X[5] + 0x6ed9eba1;
        c = c << 11 | c >>> (32 - 11);
        b += (c ^ d ^ a) + X[13] + 0x6ed9eba1;
        b = b << 15 | b >>> (32 - 15);
        a += (b ^ c ^ d) + X[3] + 0x6ed9eba1;
        a = a << 3 | a >>> (32 - 3);
        d += (a ^ b ^ c) + X[11] + 0x6ed9eba1;
        d = d << 9 | d >>> (32 - 9);
        c += (d ^ a ^ b) + X[7] + 0x6ed9eba1;
        c = c << 11 | c >>> (32 - 11);
        b += (c ^ d ^ a) + X[15] + 0x6ed9eba1;
        b = b << 15 | b >>> (32 - 15);

        a += aa;
        b += bb;
        c += cc;
        d += dd;
    }

} 
